package com.zmj.sy.mom.srv.aps.service.scheduler.resource;

import com.zmj.sy.mom.srv.aps.bean.entity.resource.*;
import com.zmj.sy.mom.srv.aps.service.scheduler.resource.shift.ShiftTypeDo;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.Assert;

import java.time.LocalDate;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Getter
@Setter
public class DefaultCalendar {

    private List<ShiftType> shiftTypes;

    private List<Shift> shifts;

    private List<BreakPeriod> breakPeriods;

    private List<WeekdayRule> shiftRules;

    private transient List<Integer> shiftTypeIds;

    private transient Map<Integer, List<Shift>> shiftMap;

    private transient Map<Integer, Shift> shiftIdMap;

    private transient Map<Integer, List<WeekdayRule>> shiftRuleMap;

    private transient Map<Integer, List<BreakPeriod>> shiftBreakPeriodMap;

    private transient Map<Integer, List<BreakPeriod>> breakPeriodMap;

    private transient Map<Integer, ShiftTypeDo> shiftTypeDoMap;

    public List<Integer> shiftTypeIds() {
        if (shiftTypeIds == null) {
            shiftTypeIds = shiftTypes.stream().map(ShiftType::getId).collect(Collectors.toList());
        }
        return shiftTypeIds;
    }

    private List<Shift> shifts(Integer shiftTypeId) {
        if (shiftMap == null) {
            shiftMap = shifts.stream()
                    .collect(Collectors.groupingBy(Shift::getShiftTypeId));
        }
        return shiftTypeId == null ? null : shiftMap.get(shiftTypeId);
    }

    private Shift shift(Integer shiftId) {
        if (shiftIdMap == null) {
            shiftIdMap = shifts.stream()
                    .collect(Collectors.toMap(Shift::getId, Function.identity()));
        }
        return shiftId == null ? null : shiftIdMap.get(shiftId);
    }

    private List<WeekdayRule> shiftRules(Integer shiftTypeId) {
        if (shiftRuleMap == null) {
            shiftRuleMap = shiftRules.stream()
                    .sorted(Comparator.comparing(WeekdayRule::getId))
                    .collect(Collectors.groupingBy(WeekdayRule::getShiftTypeId));
        }
        return shiftTypeId == null ? null : shiftRuleMap.get(shiftTypeId);
    }

    private List<BreakPeriod> breakPeriods(Integer shiftTypeId) {
        if (breakPeriodMap == null) {
            breakPeriodMap = breakPeriods == null ? new HashMap<>() : breakPeriods.stream()
                    .collect(Collectors.groupingBy(period -> {
                        Shift shift = shift(period.getShiftId());
                        if (shift == null) {
                            return -1;
                        }
                        return shift.getShiftTypeId();
                    }));
        }
        return shiftTypeId == null ? null : breakPeriodMap.get(shiftTypeId);
    }

    public ShiftTypeDo shiftTypeDo(Integer shiftTypeId) {
        if (shiftTypeDoMap == null) {
            shiftTypeDoMap = new HashMap<>();
            for (ShiftType shiftType : shiftTypes) {
                shiftTypeDoMap.put(shiftType.getId(), buildShiftTypeDo(shiftType));
            }
        }
        return shiftTypeId == null ? null : shiftTypeDoMap.get(shiftTypeId);
    }

    private ShiftTypeDo buildShiftTypeDo(ShiftType shiftType) {
        List<Shift> shifts = shifts(shiftType.getId());
        if (shifts == null || shifts.isEmpty()) {
            return null;
        }
        List<BreakPeriod> breakPeriods = breakPeriods(shiftType.getId());
        List<WeekdayRule> shiftRules = shiftRules(shiftType.getId());
        return ShiftTypeDo.of(shiftType, shifts, breakPeriods, shiftRules);
    }

    /**
     * 计算工作组在某一天的上班班次
     */
    public List<Shift> workShifts(Group group, List<Station> stations, LocalDate date) {
        Integer shiftTypeId = group.getShiftTypeId();
        if (shiftTypeId == null) {
            shiftTypeId = stations.get(0).getShiftTypeId();
        }
        ShiftTypeDo shiftTypeDo = shiftTypeDo(shiftTypeId);
        Assert.notNull(shiftTypeDo, "工作组" + group.getName() + "没有配置班制");
        return shiftTypeDo.workShifts(date);
    }

    /**
     * 计算工作组在某一天的上班班次
     */
    public List<Shift> shifts(Group group, List<Station> stations) {
        Integer shiftTypeId = group.getShiftTypeId();
        if (shiftTypeId == null) {
            shiftTypeId = stations.get(0).getShiftTypeId();
        }
        ShiftTypeDo shiftTypeDo = shiftTypeDo(shiftTypeId);
        Assert.notNull(shiftTypeDo, "工作组" + group.getName() + "没有配置班制");
        return shiftTypeDo.getShifts();
    }

}
